#include "stun_protocol_msg.h"

#include "./../string_util/string_util.h"
#include "stun_protocol_attr.h"

#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>

StunMsg::StunMsg()
{
    m_msg_type   = 0;
    m_msg_length = 0;
}

StunMsg::~StunMsg()
{
    m_msg_attr_vec.clear();
}

void StunMsg::MsgType(int type)
{
    m_msg_type = type;
}

void StunMsg::MsgLength(int length)
{
    m_msg_length = length;
}

void StunMsg::MsgMagicCookie(uint32_t magic_cookie)
{
    m_magic_cookie = magic_cookie;
}

void StunMsg::TransactionId(const std::string & transaction)
{
    m_hex_transaction_id = transaction;
}

void StunMsg::AddAttr(const std::shared_ptr<StunAttr>& attr)
{
    m_msg_attr_vec.push_back(attr);
}

uint16_t StunMsg::MsgType() const
{
    return m_msg_type;
}

uint16_t StunMsg::MsgLength() const
{
    return m_msg_length;
}

uint32_t StunMsg::MsgCookie() const
{
    return m_magic_cookie;
}

std::shared_ptr<StunAttr> StunMsg::AttrByType(uint16_t attr_type)
{
    std::shared_ptr<StunAttr> attr = nullptr;
    for (auto & data : m_msg_attr_vec)
    {
        if (data->AttrType() == attr_type)
        {
            attr = data;
            break;
        }
    }
    return attr;
}

void StunMsg::TransactionId(uint8_t * data)
{
    memcpy(m_transaction_id, data, TRANSACTION_LENGTH);
}

uint8_t * StunMsg::TransactionId()
{
    return m_transaction_id;
}

std::vector<std::shared_ptr<StunAttr>> & StunMsg::Attr()
{
    return m_msg_attr_vec;
}

void StunMsg::DumpMsgHeader() const
{
    printf("message type:0x%04x.\n", m_msg_type);
    printf("message length:%d.\n", m_msg_length);
    printf("message cookie:0x%08x.\n", m_magic_cookie);
    printf("message transaction:%s.\n", m_hex_transaction_id.c_str());
}

int StunMsg::Compose(uint8_t * data, int data_length)
{
    uint16_t type    = MsgType();
    type             = StringUtil::HtoBe16(type);
    uint16_t  len    = StringUtil::HtoBe16(MsgLength());
    uint32_t  cookie = StringUtil::BeToH32(MsgCookie());
    uint8_t * id     = TransactionId();
    uint16_t  index  = 0;
    memcpy(data, &type, sizeof(type));
    index += sizeof(type);
    memcpy(data + index, &len, sizeof(len));
    index += sizeof(len);
    memcpy(data + index, &cookie, sizeof(cookie));
    index += sizeof(cookie);
    memcpy(data + index, id, StunMsg::TRANSACTION_LENGTH);
    index += StunMsg::TRANSACTION_LENGTH;
    for (auto & attr : m_msg_attr_vec)
    {
        index += attr->ComposeAttrToBuffer(data + index, data_length - index);
    }
    // header fixed 20byte
    len = index - 20;
    len = StringUtil::BeToH16(len);
    memcpy(data + 2, &len, sizeof(len));
    return index;
}

int StunMsg::CreateTransactionID(uint8_t * result)
{
    srand(reinterpret_cast<uint64_t>(result) + rand() % 10000);
    for (int index = 0; index < TRANSACTION_LENGTH; index++)
    {
        uint8_t item      = rand() % UINT8_MAX;
        *(result + index) = item;
    }
    return TRANSACTION_LENGTH;
}
